package com.ttg.utils;

/**
 * Class IdWorker
 * Description
 * Create 2016-01-20 13:58:15
 * @author Ardy    
 */
public class IdWorker {

    /** 
     * Field twepoch
     * Description 
     */
    private final static long twepoch = 1453276528125L;

    /** 
     * Field workerIdBits
     * Description 机器标识位数
     */
    private final static long workerIdBits = 4L;

    /** 
     * Field maxWorkerId
     * Description 机器ID最大值
     */
    private final static long maxWorkerId = -1L ^ -1L << workerIdBits;

    /** 
     * Field sequenceBits
     * Description 毫秒内自增位
     */
    private final static long sequenceBits = 10L;

    /** 
     * Field workerIdShift
     * Description 机器ID偏左移N位
     */
    private final static long workerIdShift = sequenceBits;

    /** 
     * Field timestampLeftShift
     * Description 数据中心ID左移N位（毫秒内自增位+机器ID最大值）
     */
    private final static long timestampLeftShift = sequenceBits + workerIdBits;

    /** 
     * Field sequenceMask
     * Description 
     */
    private final static long sequenceMask = -1L ^ -1L << sequenceBits;

    /** 
     * Field sequence
     * Description 
     */
    private long sequence = 0L;

    /** 
     * Field lastTimestamp
     * Description 
     */
    private long lastTimestamp = -1L;

    /** 
     * Field workerId
     * Description 
     */
    private final long workerId;

    /**
     * Constructs IdWorker
     * Description
     *
     * @param workerId
     */
    public IdWorker(final long workerId) {
        super();

        if ((workerId > maxWorkerId) || (workerId < 0)) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0",
                    maxWorkerId));
        }

        this.workerId = workerId;
    }

    /**
     * Method nextId
     * Description
     * CreateDate 2016-01-20 13:58:15
     *
     * @return long
     */
    public synchronized long nextId() {
        long timestamp = this.timeGen();

        if (this.lastTimestamp == timestamp) {
            this.sequence = ( this.sequence + 1 ) & sequenceMask;

            if (this.sequence == 0) {
                System.out.println("###########" + sequenceMask);
                timestamp = this.tilNextMillis(this.lastTimestamp);
            }
        } else {
            this.sequence = 0;
        }

        if (timestamp < this.lastTimestamp) {
            try {
                throw new Exception(
                    String.format(
                        "Clock moved backwards.  Refusing to generate id for %d milliseconds",
                        this.lastTimestamp - timestamp));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        this.lastTimestamp = timestamp;

        long nextId = ( ( timestamp - twepoch << timestampLeftShift ) ) | ( this.workerId << workerIdShift )
                      | ( this.sequence );

/*        System.out.println (timestamp);
        System.out.println (twepoch<<timestampLeftShift);
        System.out.println (timestamp-twepoch<<timestampLeftShift);*/
       /* System.out.println("timestamp:" + timestamp + ",timestampLeftShift:"
         + timestampLeftShift + ",nextId:" + nextId + ",workerId:"
         + workerId + ",sequence:" + sequence);*/
        return nextId;
    }

    /**
     * Method tilNextMillis
     * Description
     * CreateDate 2016-01-20 13:58:15
     *
     * @param lastTimestamp long
     *
     * @return long
     */
    private long tilNextMillis(final long lastTimestamp) {
        long timestamp = this.timeGen();

        while (timestamp <= lastTimestamp) {
            timestamp = this.timeGen();
        }

        return timestamp;
    }

    /**
     * Method timeGen
     * Description
     * CreateDate 2016-01-20 13:58:15
     *
     * @return long
     */
    private long timeGen() {
        return System.currentTimeMillis();
    }

    /**
     * Method main
     * Description
     * CreateDate 2016-01-20 13:58:15
     *
     * @param args String[]
     *
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {
        IdWorker worker2 = new IdWorker (4);
        for (int i = 1; i < 100; i++) {
            System.out.println (worker2.nextId ());
        }
    }
}


//~ Formatted by Jindent --- http://www.jindent.com
